package com.egao.common.component.elasticsearch.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.egao.common.component.elasticsearch.entity.ElasticSearchConstants;
import com.egao.common.component.elasticsearch.entity.vo.ElasticPushMsgFormVO;
import com.egao.common.core.web.PageParam;
import com.egao.common.module.push.constants.PushConstants;
import com.egao.common.module.student.entity.vo.StudentPushStatisticsVO;
import com.egao.common.module.system.constants.DictConstants;
import com.egao.common.module.system.entity.College;
import com.egao.common.module.system.entity.Dict;
import com.egao.common.module.system.service.CollegeService;
import com.egao.common.module.system.service.DictService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.CardinalityAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedCardinality;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * ElasticSearch 推送消息统计服务
 *
 * @author dl
 */
@Slf4j
@Service
public class ElasticStatisticsService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private DictService dictService;
    @Autowired
    private CollegeService collegeService;

    /**
     * 推荐总人次
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushCountTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        // 搜索的索引
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        SearchRequest searchRequest = getSearchRequest(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return response.getHits().getTotalHits().value;
    }

    /**
     * 推荐总阅读数
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushReadTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        // 已读
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setReadFlag(PushConstants.YES);
        SearchRequest searchRequest = getSearchRequest(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return response.getHits().getTotalHits().value;
    }

    /**
     * 推荐总阅读学生数
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushReadStudentTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey("studentId");
        ep.setReadFlag(PushConstants.YES);
        SearchRequest searchRequest = getSearchRequestDistinct(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ParsedCardinality result = response.getAggregations().get("studentId");
        return result.getValue();
    }

    /**
     * 推荐总发送简历数
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushResumeTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        // 已发送
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setResumeFlag(PushConstants.YES);
        SearchRequest searchRequest = getSearchRequest(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return response.getHits().getTotalHits().value;
    }

    /**
     * 推荐总发送简历学生数
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushResumeStudentTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey("studentId");
        ep.setResumeFlag(PushConstants.YES);
        SearchRequest searchRequest = getSearchRequestDistinct(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ParsedCardinality result = response.getAggregations().get("studentId");
        return result.getValue();
    }

    /**
     * 推荐信息总数
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushBusinessTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey("businessId");
        SearchRequest searchRequest = getSearchRequestDistinct(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ParsedCardinality result = response.getAggregations().get("businessId");
        return result.getValue();
    }

    /**
     * 职位总数
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushPositionTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey("positions.id");
        SearchRequest searchRequest = getSearchRequestDistinct(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ParsedCardinality result = response.getAggregations().get("positions.id");
        return result.getValue();
    }

    /**
     * 推荐信息单位总数
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushCompanyTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey("companyId");
        SearchRequest searchRequest = getSearchRequestDistinct(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ParsedCardinality result = response.getAggregations().get("companyId");
        return result.getValue();
    }

    /**
     * 推荐信息接收学生总数
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public Long pushStudentTotal(ElasticPushMsgFormVO elasticPushMsgFormVO) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey("studentId");
        SearchRequest searchRequest = getSearchRequestDistinct(ep);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ParsedCardinality result = response.getAggregations().get("studentId");
        return result.getValue();
    }

    /**
     * 统计省份
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public List<Map<String, Object>> getListByKey(ElasticPushMsgFormVO elasticPushMsgFormVO, String key, int size) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey(key);
        SearchRequest searchRequest = getSearchRequestAggregation(ep, size);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Terms terms = response.getAggregations().get(ep.getKey());
        List<Map<String, Object>> dataList = new ArrayList<>();
        for (Terms.Bucket bucket : terms.getBuckets()) {
            if (StrUtil.isNotBlank(bucket.getKeyAsString())) {
                Map<String, Object> map = new HashMap<>(16);
                map.put("name", bucket.getKeyAsString());
                map.put("value", bucket.getDocCount());
                dataList.add(map);
            }
        }
        return dataList;
    }

    /**
     * 统计单位性质
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @param key                  分组关键字
     * @param type                 字典集合
     * @return
     */
    public List<Map<String, Object>> getCompanyDict(ElasticPushMsgFormVO elasticPushMsgFormVO, String key, String type) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey(key);
        List<Dict> natureList = dictService.cacheNatureList(type);
        SearchRequest searchRequest = getSearchRequestAggregation(ep, 100);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Terms terms = response.getAggregations().get(ep.getKey());
        List<Map<String, Object>> dataList = new ArrayList<>();
        for (Terms.Bucket bucket : terms.getBuckets()) {
            if (StrUtil.isNotBlank(bucket.getKeyAsString())) {
                Map<String, Object> map = new HashMap<>(16);
                String name = getDictLabel(bucket.getKeyAsString(), natureList);
                if (StrUtil.isNotBlank(name)) {
                    map.put("name", name);
                    map.put("value", bucket.getDocCount());
                    dataList.add(map);
                }
            }
        }
        return dataList;
    }

    /**
     * 查询字典名称
     *
     * @param value    字典值
     * @param dictList 字典集合
     * @return
     */
    private String getDictLabel(String value, List<Dict> dictList) {
        for (Dict dict : dictList) {
            if (value.equals(dict.getValue())) {
                return dict.getLabel();
            }
        }
        return null;
    }

    /**
     * 统计职位
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    public List<Map<String, Object>> getPosition(ElasticPushMsgFormVO elasticPushMsgFormVO, int size) throws IOException {
        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(elasticPushMsgFormVO);
        ep.setKey("positions.name");
        SearchRequest searchRequest = getSearchRequestAggregation(ep, size);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Terms terms = response.getAggregations().get(ep.getKey());
        List<Map<String, Object>> dataList = new ArrayList<>();
        for (Terms.Bucket bucket : terms.getBuckets()) {
            if (StrUtil.isNotBlank(bucket.getKeyAsString())) {
                Map<String, Object> map = new HashMap<>(16);
                map.put("name", bucket.getKeyAsString());
                map.put("value", bucket.getDocCount());
                dataList.add(map);
            }
        }
        return dataList;
    }

    /**
     * 分学院统计学生推送信息
     *
     * @param
     * @return
     */
    public List<StudentPushStatisticsVO> statisticsStudentPushByCollege(PageParam<ElasticPushMsgFormVO> pageParam) throws IOException {
        List<College> collegeList = collegeService.listAll(null, "");
        List<StudentPushStatisticsVO> list = new ArrayList<>();
        for (College college : collegeList) {
            list.add(new StudentPushStatisticsVO(college.getCode(), college.getName()));
        }

        ElasticPushMsgFormVO pushMsgFormVO = new ElasticPushMsgFormVO();

        if (pageParam.get(PushConstants.APPLY_TIME) != null) {
            pushMsgFormVO.setApplyTime((String) pageParam.get(PushConstants.APPLY_TIME));
        }
        if (pageParam.get(PushConstants.COLLEGE_CODE) != null) {
            pushMsgFormVO.setCollegeCode((String) pageParam.get(PushConstants.COLLEGE_CODE));
        }
        if (pageParam.get(PushConstants.TYPE) != null) {
            pushMsgFormVO.setType((String) pageParam.get(PushConstants.TYPE));
        }
        if (pageParam.get(PushConstants.YEAR_ID) != null) {
            pushMsgFormVO.setYearId(Integer.parseInt(pageParam.get(PushConstants.YEAR_ID).toString()));
        }

        ElasticPushMsgFormVO ep = copyElasticPushMsgFormVO(pushMsgFormVO);
        ep.setKey("collegeCode");

        // 总
        SearchRequest searchRequest = getSearchRequestAggregation(ep, 100);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Terms terms = response.getAggregations().get(ep.getKey());
        for (Terms.Bucket bucket : terms.getBuckets()) {
            if (StrUtil.isBlank(bucket.getKeyAsString())) {
                continue;
            }

            for (StudentPushStatisticsVO item : list) {
                if (item.getCollegeCode().equals(bucket.getKeyAsString())) {
                    item.setCountPush(bucket.getDocCount());
                }
            }
        }

        // 浏览
        ep.setReadFlag(PushConstants.YES);
        SearchRequest searchRequest1 = getSearchRequestAggregation(ep, 100);
        SearchResponse response1 = restHighLevelClient.search(searchRequest1, RequestOptions.DEFAULT);
        Terms terms1 = response1.getAggregations().get(ep.getKey());
        for (Terms.Bucket bucket : terms1.getBuckets()) {
            if (StrUtil.isBlank(bucket.getKeyAsString())) {
                continue;
            }

            for (StudentPushStatisticsVO item : list) {
                if (item.getCollegeCode().equals(bucket.getKeyAsString())) {
                    item.setCountHits(bucket.getDocCount());
                }
            }
        }

        // 简历
        ep.setReadFlag("");
        ep.setResumeFlag(PushConstants.YES);
        SearchRequest searchRequest2 = getSearchRequestAggregation(ep, 100);
        SearchResponse response2 = restHighLevelClient.search(searchRequest2, RequestOptions.DEFAULT);
        Terms terms2 = response2.getAggregations().get(ep.getKey());
        for (Terms.Bucket bucket : terms2.getBuckets()) {
            if (StrUtil.isBlank(bucket.getKeyAsString())) {
                continue;
            }

            for (StudentPushStatisticsVO item : list) {
                if (item.getCollegeCode().equals(bucket.getKeyAsString())) {
                    item.setCountResume(bucket.getDocCount());
                }
            }
        }

        long countPush = 0L;
        long countHits = 0L;
        long countResume = 0L;
        for (StudentPushStatisticsVO item : list) {
            countPush += item.getCountPush();
            countHits += item.getCountHits();
            countResume += item.getCountResume();
        }
        StudentPushStatisticsVO statisticsVO = new StudentPushStatisticsVO("0", "合计");
        statisticsVO.setCountPush(countPush);
        statisticsVO.setCountHits(countHits);
        statisticsVO.setCountResume(countResume);
        list.add(0, statisticsVO);

        return list;
    }


    /**
     * 构建分组SearchRequest Distinct
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    private SearchRequest getSearchRequestAggregation(ElasticPushMsgFormVO elasticPushMsgFormVO, int size) {
        String key = elasticPushMsgFormVO.getKey();
        // 搜索的索引
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConstants.INDEX_NAME_PUSH_MSG);
        TermsAggregationBuilder aggregation = AggregationBuilders
                //别名
                .terms(key)
                //聚合字段名
                .field(key + ".keyword")
                //降序
                .order(BucketOrder.count(false))
                //聚合结果数据量，默认只返回前十条
                .size(size);
        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(elasticPushMsgFormVO);
        //指定count(distinct)字段名,cardinality为指定字段的别名,field为指定字段
        searchSourceBuilder.aggregation(aggregation);
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

    /**
     * 构建去重SearchRequest Distinct
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    private SearchRequest getSearchRequestDistinct(ElasticPushMsgFormVO elasticPushMsgFormVO) {
        String key = elasticPushMsgFormVO.getKey();
        // 搜索的索引
        SearchRequest request = new SearchRequest(ElasticSearchConstants.INDEX_NAME_PUSH_MSG);
        CardinalityAggregationBuilder aggregationBuilder = AggregationBuilders.cardinality(key).field(key);
        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(elasticPushMsgFormVO);
        //指定count(distinct)字段名,cardinality为指定字段的别名,field为指定字段
        searchSourceBuilder.aggregation(aggregationBuilder);
        request.source(searchSourceBuilder);
        return request;
    }

    /**
     * 构建SearchRequest
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    private SearchRequest getSearchRequest(ElasticPushMsgFormVO elasticPushMsgFormVO) {
        // 搜索的索引
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConstants.INDEX_NAME_PUSH_MSG);
        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(elasticPushMsgFormVO);
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

    /**
     * 构建SearchSourceBuilder
     *
     * @param elasticPushMsgFormVO 搜索条件
     * @return
     */
    private SearchSourceBuilder getSearchSourceBuilder(ElasticPushMsgFormVO elasticPushMsgFormVO) {

        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 复合查询 should或者 must并且
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 信息类型相等 term 完全匹配
        // 单位同意推送
        boolQueryBuilder.must(QueryBuilders.termQuery("companyFlag", PushConstants.YES));
        // 学校同意
        boolQueryBuilder.must(QueryBuilders.termQuery("adminFlag", PushConstants.YES));
        if (StrUtil.isNotBlank(elasticPushMsgFormVO.getType())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("type", elasticPushMsgFormVO.getType()));
        }

        if (StrUtil.isNotBlank(elasticPushMsgFormVO.getCollegeCode())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("collegeCode", elasticPushMsgFormVO.getCollegeCode()));
        }

        if (elasticPushMsgFormVO.getCompanyId() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("companyId", elasticPushMsgFormVO.getCompanyId()));
        }

        if (elasticPushMsgFormVO.getStudentId() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("studentId", elasticPushMsgFormVO.getStudentId()));
        }

        if (elasticPushMsgFormVO.getYearId() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("yearId", elasticPushMsgFormVO.getYearId()));
        }

        if (elasticPushMsgFormVO.getBusinessId() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("businessId", elasticPushMsgFormVO.getBusinessId()));
        }

        if (StrUtil.isNotBlank(elasticPushMsgFormVO.getApplyTime())) {
            String[] arr = elasticPushMsgFormVO.getApplyTime().split("~");
            Date startTime = DateUtil.parse(arr[0] + " 00:00:00", "yyyy-MM-dd HH:mm:ss");
            Date endTime = DateUtil.parse(arr[1] + " 23:59:59", "yyyy-MM-dd HH:mm:ss");
            boolQueryBuilder.must(QueryBuilders.rangeQuery("pushDate").gte(startTime.getTime()).lte(endTime.getTime()));
        }

        if (StrUtil.isNotBlank(elasticPushMsgFormVO.getReadFlag())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("readFlag", elasticPushMsgFormVO.getReadFlag()));
        }


        if (StrUtil.isNotBlank(elasticPushMsgFormVO.getResumeFlag())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("resumeFlag", elasticPushMsgFormVO.getResumeFlag()));
        }
        // 设置为true就会返回真实的命中条数
        searchSourceBuilder.trackTotalHits(true);
        searchSourceBuilder.query(boolQueryBuilder);

        // source builder 10秒timeout
        searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));
        return searchSourceBuilder;
    }

    private ElasticPushMsgFormVO copyElasticPushMsgFormVO(ElasticPushMsgFormVO elasticPushMsgFormVO) {
        ElasticPushMsgFormVO ep = new ElasticPushMsgFormVO();
        ep.setType(elasticPushMsgFormVO.getType());
        ep.setApplyTime(elasticPushMsgFormVO.getApplyTime());
        ep.setCollegeCode(elasticPushMsgFormVO.getCollegeCode());
        if (elasticPushMsgFormVO.getCompanyId() != null) {
            ep.setCompanyId(elasticPushMsgFormVO.getCompanyId());
        }
        if (elasticPushMsgFormVO.getStudentId() != null) {
            ep.setStudentId(elasticPushMsgFormVO.getStudentId());
        }
        if (elasticPushMsgFormVO.getBusinessId() != null) {
            ep.setBusinessId(elasticPushMsgFormVO.getBusinessId());
        }
        if (elasticPushMsgFormVO.getYearId() != null) {
            ep.setYearId(elasticPushMsgFormVO.getYearId());
        }
        return ep;
    }
}
